using DotNetty.Codecs;
using DotNetty.Codecs.Protobuf;
using DotNetty.Handlers.Logging;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System;
using System.Net;
using System.Threading.Tasks;

/// <summary>
/// 客户端
/// </summary>
public class TcpClient
{
    private IChannel channel;

    /// <summary>
    /// 是否已连接
    /// </summary>
    public bool Connected { get; private set; }

    /// <summary>
    /// 初始化
    /// </summary>
    public TcpClient()
    {
        //Set the following properties to optimize performance and GC
        if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("io.netty.leakDetection.level")))
            Environment.SetEnvironmentVariable("io.netty.leakDetection.level", "Disabled");
        if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("io.netty.noPreferDirect")))
            Environment.SetEnvironmentVariable("io.netty.noPreferDirect", "false");
        if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("io.netty.allocator.maxOrder")))
            Environment.SetEnvironmentVariable("io.netty.allocator.maxOrder", "9");
        if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("io.netty.recycler.ratio")))
            Environment.SetEnvironmentVariable("io.netty.recycler.ratio", "0");
    }

    /// <summary>
    /// 打开连接
    /// </summary>
    /// <param name="host"></param>
    /// <param name="port"></param>
    /// <returns></returns>
    public async Task<IChannel> ConnectAsync(string host, int port)
    {
        MultithreadEventLoopGroup group = new MultithreadEventLoopGroup();
        try
        {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap
                .Group(group)
                .Channel<TcpSocketChannel>()
                .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                {
                    IChannelPipeline pipeline = channel.Pipeline;
                    // pipeline.AddLast("Log", new LoggingHandler(LogLevel.INFO));
                    pipeline.AddLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 4, 0, 4));
                    pipeline.AddLast(new ProtobufDecoder(Messages.IMessage.Parser));
                    pipeline.AddLast(new LengthFieldPrepender(4));
                    pipeline.AddLast(new ProtobufEncoder());
                    pipeline.AddLast(new TcpClientHandler());
                }));
            channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(host), port));
            this.Connected = true;
            _ = channel.CloseCompletion.ContinueWith(async (t) =>
            {
                if (group != null)
                    await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)).ConfigureAwait(false);
            });
            return channel;
        }
        catch (Exception)
        {
            try
            {
                if (channel != null)
                {
                    await channel.CloseAsync().ConfigureAwait(false);
                }
            }
            catch (Exception) { }
            if (group != null)
            {
                await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)).ConfigureAwait(false);
            }
            this.Connected = false;
            throw;
        }
    }

    /// <summary>
    /// 断开连接
    /// </summary>
    /// <returns></returns>
    public async Task DisconnectAsync()
    {
        if (this.channel != null)
        {
            await channel.DisconnectAsync().ConfigureAwait(false);
        }
        this.Connected = false;
    }
}
